C++은 Java와 .net과 달리 가비지 컬렉터(garbage collection) 기능을 기본 제공하지 않는다.
수동으로 메모리를 관리할 수 있다.
메모리 할당 및 메모리 해제 루틴(operator new & operator delete)
+ new-handler(operator new가 정상 수행되지 못했을 때, 호출되는 함수)
동적으로 할당되는 자원(힙에 할당)은 수정이 가능한 전역 자원으로 분류된다.
다중 스레드 환경에서는 전역 자원에 대한 접근 경쟁 상태가 생길 수 있다.
new 처리자 동작 순서operator new 함수가 정상적으로 수행하지 못할 경우,
operator new는 예외를 던진다(C에서 cmalloc계열 함수는 메모리 할당 실패시 nullptr를 반환한다.)
operator new가 예외를 던지기 전에 에러 처리 함수를 우선적으로 호출한다.
new 처리자(new handler)는 사용자가 지정할 수 있도록, 표준 라이브러리는 set_new_handler라는
함수를 제공한다.
namespace std{
typedef void(*new_handler)();
new_handler set_new_handler(new_handler p) throw();
}
new_handler는 인자가 없고, 반환값도 없는 함수 포인터에 대한 typedef이다.
set_new_handler는 new_handler를 매개변수로 하고, new_handler를 반환하는 함수이다.
set_new_handler의 new_handler 타입의 매개변수가 메모리 할당을 실패했을 때,
호출하게 되는 함수의 포인터이다.
set_new_handler가 호출되기 바로 전까지 new 처리자가 사용하고 있던 함수의 포인터를 반환
void outOfMem(){
std::cerr<<"Unable to satisfy request for memory\n";
std::abort();
}
int main(void){
std::set_new_handler(outOfMem);
int* pBigDtaArray=new int[1000000000L];
}
new handler 구현- 사용할 수 있는 메모리를 더 많이 확보
프로그램이 시작할 때, 메모리 블록을 크게 하나 할당해 두었다가, new 처리자가 처음 호출될 때,
해당 메모리를 사용할 수 있게 해준다.
- 다른 new 처리자를 설치
현재의 new handler 내에서 set_new_handler를 호출한다.
operator new가 다시 new handler를 호출하면, 새로운 처리 함수를 호출하게 된다.
- new 처리자의 설치를 제거
set_new_handler에게 nullptr를 전달한다.
new handler에 함수가 지정되어 있지 않으면, 메모리 할당이 실패할 때, 예외를 던진다.
- 예외를 던진다.
bad_alloc 혹은 bad_alloc에서 파생된 타입의 예외를 던진다.
- 복귀하지 않는다.
abort() 혹은 exit()를 호출
class X{
public:
static void outOfMemory();
};
class Y{
public:
static void outOfMemory();
};
X* p1=new X;
Y* p2=new Y;
위와 같이 객체의 종류에 따라 다른 new_handler를 지정하고 싶은 경우,
해당 클래스에서 자체 버전의 set_new_handler 및 operator new를 제공하도록 해주면 된다.
class Widget{
public:
static std::new_handler set_new_handler(std::new_handler p) throw();
static void* operator new(std::size_t size) throw(std::bad_alloc);
private:
static std::new_handler currentHandler;
};
std::new_handler Widget::currentHandler=0;
std::new_handler Widget::set_new_handler(std::new_handler p) throw(){
std::new_handler oldHandler=currentHandler;
currentHandler=p;
return oldHandler;
}
1. 표준 set_new_handler 함수에 Widget new-handler를 넘겨서 호출한다.
(전역 new 처리자로써 Widget의 new-handler를 호출할 것임)
2. 전역 operator new를 호출하고 실패할 시, Widget:new_handler를 호출한다.
(이 후에도 실패시, 이전의 oldHandler로 전역 new-handler를 전환 후, bad_alloc 예외를 호출)
3. 전역 operator new를 호출하고 성공 시, 할당된 메모리를 반환한다.
class NewHandlerHolder{
public:
explicit NewHandlerHolder(std::new_handler nh): handler(nh) {}
~NewHandlerHolder(){
std::set_new_handler(handler);
}
private:
std::new_handler handler;
NewHandlerHolder(const NewHandlerHolder&);
NewHandlerHolder& operator=(const NewHandlerHolder&); 복사 금지
};
void* Widget::operator new(std::size_t size) throw(std::bad_alloc){
NewHandlerHolder h(std::set_new_handler(currentHandler));
return ::operator new(size); 전역 operator new
}
void outOfMem();
Widget::set_new_handler(outOfMem);
Widget* pw1=new Widget;
std::string* ps=new std::string;
Widget::set_new_handler(0);
Widget* pw2=new Widget;
믹스인(mix-in) 양
식다른 파생 클래스들이 한 가지의 특정 기능만을 물려받아 갈 수 있도록 설계된 기본 클래스
set_new_handler 함수와 operator new 함수를 상속한다.
template <typename T>
class NewHandlerSupport{
public:
static std::new_handler set_new_handler(std::new_handler p) throw();
static void* operator new(std::size_t size) throw(std::bad_alloc);
private:
static std::new_handler currentHandler;
};
template <typename T>
std::new_handler NewHandlerSupport<T>::set_new_handler(std::new_handler p) throw(){
std::new_handler oldHandler=currentHandler;
currenthandler=p;
return oldHandler;
}
template <typename T>
void* NewHandlerSupport<T>::operator new(std::size_t size) throw(std::bad_alloc){
NewHandlerHolder h(std::set_new_handler(currentHandler));
return ::operator new(size);
}
template <typename T>
std::new_handler NewHandlerSupport<T>::currentHandler=0;
이 후 위의 NewHandlerSupport<T>를 상속해서, 사용할 수 있다.
class Widget: public NewHandlerSupport<Widget>{
};
NewHandlerSupport 템플릿은 타입 매개변수 T를 사용하지는 않는다.
매개변수 T는 파생 클래스를 구분해 주는 역할만 함
(NewHandlerSupport가 인스턴스화 될 때, 전달되는 T를 위한 currentHandler 사본을 생성)
C에서 동적 메모리 할당을 실패하면, null을 반환할 뿐 예외를 던지지는 않는다.
C++에서 new를 통해 메모리 할당 할 때도 이와 같이 예외를 던지지 않도록 할 수 있다.
“예외 불가(nothrow) 형태"
class Widget{ };
Widget* pw1=new Widget;
Widget* pw2=new (std::nothrow) Widget;
if(pw2==0)
std::nothrow는 <new>에 정의